package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
)

// Booking 包含绑定和验证的数据。
type Booking struct {
	CheckIn  time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"`
	CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn,bookabledate" time_format:"2006-01-02"`
}

var bookableDate validator.Func = func(fl validator.FieldLevel) bool {
	date, ok := fl.Field().Interface().(time.Time)
	if ok {
		today := time.Now()
		if today.After(date) {
			return false
		}
	}
	return true
}

func main() {
	route := gin.Default()
	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
		log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
	}

	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		v.RegisterValidation("bookabledate", bookableDate)
	}

	//bookable?check_in=2036-01-02&check_out=2036-01-03
	route.GET("/bookable", getBookable)

	route.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "Hello World!")
	})

	route.GET("/long_async", long_async)
	route.GET("/long_sync", long_sync)

	{
		v1 := route.Group("/v1")
		{
			v1.GET("/", hello)
		}
	}

	{
		v2 := route.Group("/v2")
		{
			v2.GET("/", hello2)
		}
	}

	route.GET("/test", func(c *gin.Context) {
		log.Println("test")
		c.Request.URL.Path = "/test2"
		route.HandleContext(c)
	})
	route.GET("/test2", func(c *gin.Context) {
		log.Println("test2")
		c.JSON(200, gin.H{"hello": "world"})
	})

	route.Run(":8080")
}

func hello(c *gin.Context) {
	c.String(http.StatusOK, "Hello World!")
}

func hello2(c *gin.Context) {
	c.String(http.StatusOK, "Hello World!")
}

func getBookable(c *gin.Context) {
	var b Booking
	if err := c.ShouldBindWith(&b, binding.Query); err == nil {
		c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"})
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	}
}

func long_async(c *gin.Context) {
	// 创建在 goroutine 中使用的副本
	cCp := c.Copy()
	go func() {
		// 用 time.Sleep() 模拟一个长任务。
		time.Sleep(5 * time.Second)

		// 请注意您使用的是复制的上下文 "cCp"，这一点很重要
		log.Println("Done! in path " + cCp.Request.URL.Path)
	}()

	log.Println("long_async")
}

func long_sync(c *gin.Context) {
	// 用 time.Sleep() 模拟一个长任务。
	time.Sleep(5 * time.Second)

	// 因为没有使用 goroutine，不需要拷贝上下文
	log.Println("Done! in path " + c.Request.URL.Path)
	log.Println("long_sync")
}
